package com.common.sdk.net.download.http;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.conn.ConnectTimeoutException;

import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import com.android.sohu.sdk.common.toolbox.LogUtils;
import com.android.sohu.sdk.common.toolbox.NetworkUtils;
import com.android.sohu.sdk.common.toolbox.StorageUtils;
import com.common.sdk.net.download.callback.CallbackObservable;
import com.common.sdk.net.download.callback.error.CallbackEnum;
import com.common.sdk.net.download.callback.error.DownloadState;
import com.common.sdk.net.download.db.ControlCacheAndDb;
import com.common.sdk.net.download.interfaces.IDownloadNetwork;
import com.common.sdk.net.download.request.DownloadRequest;
import com.common.sdk.net.download.request.model.DownloadInfo;
import com.common.sdk.net.download.util.DownloadFilePathUtils;
import com.common.sdk.net.download.util.DownloadLog;
import com.common.sdk.net.connect.http.HttpStack;
import com.common.sdk.net.connect.http.HurlStack;
import com.common.sdk.net.connect.http.RetryPolicy;
import com.common.sdk.net.connect.http.center.tools.HttpLog;
import com.common.sdk.net.connect.http.error.AuthFailureError;
import com.common.sdk.net.connect.http.error.VolleyError;
import com.common.sdk.net.connect.http.util.ByteArrayPool;
import com.common.sdk.net.connect.http.util.VolleyLog;

import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * 下载文件联网重写  TODO 优化代码结构
 * @author wenlonglv
 *
 */
public abstract class AbsNetwork implements IDownloadNetwork {
    /**每次缓存大小*/
    protected static int DEFAULT_POOL_SIZE = 1024 * 100;
    protected static int DEFAULT_BUFFER_SIZE = 1024 * 100;

    protected static final int DEFAULT_RETRY_COUNT = 3;

    public static int DEFAULT_THREADID = -1;

    protected final HttpStack mHttpStack;
    /**缓存字节，节约内存*/
    protected final ByteArrayPool mPool;
    HttpResponse httpResponse = null;
    Response okhttpResponse = null;

    protected static int result = DownloadState.NET_ERROR_NO_NET_NO_UPLOAD;

    public AbsNetwork(HttpStack httpStack) {
        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
    }

    public AbsNetwork(HttpStack httpStack, ByteArrayPool pool) {
        this.mHttpStack = httpStack;
        this.mPool = pool;
    }

    public HttpStack getmHttpStack() {
        return mHttpStack;
    }

    @Override
    public int performRequest(DownloadRequest request) throws Exception {
        while (true) {
            httpResponse = null;
            try {
                return initRequest(request, DEFAULT_RETRY_COUNT);
            }
//            catch (SocketTimeoutException e) {
////                attemptRetryOnException("socket", request, new TimeoutError());
//            } catch (ConnectTimeoutException e) {
////                attemptRetryOnException("connection", request, new TimeoutError());
//            } catch (MalformedURLException e) {
////                throw new RuntimeException("Bad URL " + request.getUrlWithQueryString(), e);
//            }
            catch (SocketException e){
                // 无网导致的下载失败不上报
                return DownloadState.NET_ERROR_NO_NET_NO_UPLOAD;
            } catch (IOException e) {
                HttpLog.error(e);
                DownloadLog.e(e);
                int statusCode = 0;
                // NetworkResponseEx networkResponse = null;
                if (okhttpResponse != null) {
                    statusCode = okhttpResponse.code();
                } else {// TODO 没有网络的处理逻辑
                    HttpLog.error(e);
                    // return DownloadState.NET_ERROR_NO_NET;
                    // throw new NoConnectionError(e);
                }
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrlWithQueryString());
//                if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) {
//                    attemptRetryOnException("auth", request, new AuthFailureError(/*networkResponse*/));
//                } else if (statusCode == HttpStatus.SC_OK) {
//                    if (e instanceof SocketException) {// TODO 还需要测试 下载中网络连接超时
//                        attemptRetryOnException("io socket", request, new TimeoutError());
//                    }
//                } else {
//                    // TODO: Only throw ServerError for 5xx status codes.
                    return DownloadState.NET_ERROR_NO_NET;
//
//                }
//                DownloadLog.error("performRequest while");
            }
            catch (Exception e){
                DownloadLog.debug("performRequest OnException");
                retryDownloadForException(request, 3);
            }

        }
    }

    /**
     * 是否成功
     * @param request
     * @return true 成功
     * @throws IOException
     * @throws VolleyError
     * @throws AuthFailureError
     */
    protected int initRequest(DownloadRequest request, int retryCount) throws IOException, VolleyError {
        return initRequest(request, 1, retryCount);
    }

    /**
     * 是否成功
     * @param request
     * @return true 成功
     * @throws IOException
     * @throws VolleyError
     * @throws AuthFailureError
     */
    protected abstract int initRequest(DownloadRequest request, int threadId, int retryCount) throws IOException, VolleyError;

    protected HttpResponse httpResponse(DownloadRequest request, String startPos, String endPos) throws IOException,
            AuthFailureError, Exception {
        DownloadLog.debug("lakshjfgdlajs" + "startPos:" + startPos + "...endPos:" + endPos);

        Map<String, String> headers = null;
        if (!TextUtils.isEmpty(startPos) && !TextUtils.isEmpty(endPos)) {
            headers = new HashMap<String, String>();// 下载的起始位置和结束位置
            headers.put(HurlStack.STAR_TPOS, startPos);
            headers.put(HurlStack.END_POS, endPos);
        }
        httpResponse = mHttpStack.performRequest(request, headers);
        DownloadLog.debug("Network request success" + System.nanoTime());
        StatusLine statusLine = httpResponse.getStatusLine();
        int statusCode = statusLine.getStatusCode();
        DownloadLog.debug(" statusCode : " + statusCode);
        if (statusCode < 200 || statusCode > 299) {// TODO 需要处理
            DownloadLog.debug("(statusCode < 200 || statusCode > 299)" + statusCode);
            throw new IOException();
        }
        return httpResponse;
    }
    protected Response okhttpResponse(DownloadRequest request, long currentOffset, long endOffset) throws IOException,
            AuthFailureError {
        DownloadLog.debug("lakshjfgdlajs" + "startPos:" + currentOffset + "...endPos:" + endOffset);


        final OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();

        Request.Builder builder = new Request.Builder()
                .url(request.getUrl());
        if (endOffset > 0 && currentOffset > 0) {
            final String range;
//            if (endOffset == 0) {
//                range = String.format(Locale.ENGLISH,"range[%d-)", currentOffset);
//            } else {
            range = String.format(Locale.getDefault(),"bytes=%d-%d", currentOffset, endOffset);
//            }
            DownloadLog.debug(" okhttpResponse range : " + range);
            builder.addHeader("Range",range);
        }


        Request okhttpRequest =builder
                .build();
        okhttpResponse = client.newCall(okhttpRequest).execute();
        if (!okhttpResponse.isSuccessful()) {
            DownloadLog.debug(" statusCode : " + okhttpResponse.code());
            throw new IOException("Unexpected code " + okhttpResponse);
        }

        DownloadLog.debug("Network request success" + System.nanoTime());

        Headers responseHeaders = okhttpResponse.headers();
        for (int i = 0; i < responseHeaders.size(); i++) {
            DownloadLog.debug(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }

        return okhttpResponse;
    }

    /**
     * Attempts to prepare the request for a retry. If there are no more
     * attempts remaining in the request's retry policy, a timeout exception is
     * thrown.
     *
     * @param request The request to use.
     */
    protected static void attemptRetryOnException(String logPrefix, DownloadRequest request, VolleyError exception)
            throws VolleyError {
        RetryPolicy retryPolicy = request.getRetryPolicy();
        int oldTimeout = request.getCurrentTimeoutMs();

        try {
            retryPolicy.retry(exception);
        } catch (VolleyError e) {
            request.addMarker(String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
            throw e;
        }
        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    }

    public void retryDownloadForException(final DownloadRequest request , int retryCount) {
        DownloadLog.debug("retryDownloadForException retryCount: " + retryCount);

        if (retryCount > 0) {
            final int count = retryCount -1;
            if (Looper.myLooper() == null) {
                Looper.prepare();
            }
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    try {
                        int num = initRequest(request, count);
                        callbackObservable(num, request.getDownloadInfo());
                    } catch (Exception e) {
                        DownloadLog.debug("retryDownloadForException on Exception");
                        DownloadLog.e(e);
                        retryDownloadForException(request, count);
                    }
                }
            },1000);
            Looper.loop();


        }


    }

    protected abstract int noDbDataAndNoSdData(DownloadRequest request, int retryCount) throws IOException, VolleyError;

    protected void recycleStream(InputStream is, BufferedOutputStream fos) {
        try {
            if (is != null)
                is.close();
        } catch (IOException e) {
            DownloadLog.e(e);
        }
        try {
            if (fos != null)
                fos.close();
        } catch (IOException e) {
            DownloadLog.e(e);
        }
    }

    private void callbackObservable(int num, DownloadInfo downloadinfo) {
        switch (num) {
            case DownloadState.DOWNLOAD_THREAD_DELETY_SUCCESS:
                DownloadFilePathUtils.deleteFile(downloadinfo.getDownloadFilePath());
                ControlCacheAndDb.deleteDownloadItemInThread(downloadinfo);
                break;
            case DownloadState.DATABASE_ERROR_NO:
                DownloadFilePathUtils.deleteFile(downloadinfo.getDownloadFilePath());
                ControlCacheAndDb.deleteDownloadFile(downloadinfo);
                break;
            case DownloadState.DATABASE_ERROR_NO_FILE:
                ControlCacheAndDb.deleteDownloadMapItem(downloadinfo);
                break;
            case DownloadState.DOWNLOAD_SUCCESS:
                ControlCacheAndDb.beginDownload(downloadinfo);
                CallbackObservable.getIntence().notifyObservers(CallbackEnum.FINISHED_DOWNLOAD, downloadinfo);
                break;
            case DownloadState.DOWNLOAD_STOP_SUCCESS:
                CallbackObservable.getIntence().notifyObservers(CallbackEnum.DID_STOP_DOWNLOAD_ITEM, downloadinfo);
                break;
            case DownloadState.DOWNLOAD_PAUSE_SUCCESS:
                CallbackObservable.getIntence().notifyObservers(CallbackEnum.DID_PAUSE_DOWNLOAD_ITEM, downloadinfo);
                break;
            case DownloadState.DOWNLOAD_SILENCE_PAUSE_SUCCESS:
                ControlCacheAndDb.startSilenceDownloadItem(downloadinfo);
                CallbackObservable.getIntence().notifyObservers(CallbackEnum.DID_STOP_DOWNLOAD_ITEM, downloadinfo);
                break;
            default:// 所有的错误都是在这，stop应用
                downloadinfo.stopDownload();
                ControlCacheAndDb.beginDownload(downloadinfo);
                CallbackObservable.getIntence().notifyObservers(num, CallbackEnum.FAILED_DOWNLOAD, downloadinfo);
                break;
        }

    }



}
